package com.tutou.ikgroup.websocket.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class NettyWebsocket {
    private static final Logger logger = LoggerFactory.getLogger(NettyWebsocket.class);

    @Value("${netty.websocket.port}")
    private int port;

    @Value("${netty.websocket.path}")
    private String path;

    @Value("${netty.websocket.max-frame-size}")
    private int maxFrameSize;

    @Autowired
    private HandlerWebSocket handlerWebSocket;

    @Autowired
    private HandleHeartBeat handleHeartBeat;

    @Autowired
    private HandlerHttp handlerHttp;


    @Async
    public void startNetty() {
        //创建两个线程，BossGroup 处理接收连接事件，WorkerGroup 处理 I/O 事件
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        try {
            //绑定端口
            //ServerBootstrap 用于服务器端
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss, worker)

                    //Netty 使用它来接受新的连接，NioServerSocketChannel主要用于监听和接受客户端的连接请求。
                    .channel(NioServerSocketChannel.class)

                    //配置新通道,SocketChannel 是 Netty 中用于客户端和服务器之间通信的通道类型。
                    //每当有新连接建立时，Netty 都会调用 initChannel 方法。
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {

                            //所有的 ChannelHandler 都会被添加到这个链中
                            ChannelPipeline pipeline = socketChannel.pipeline();

                            //对HTTP协议的支持，添加http编码器、解码器
                            pipeline.addLast(new HttpServerCodec())

                                    //支持异步写大数据流
                                    .addLast(new ChunkedWriteHandler())

                                    //将多个 HTTP 消息合并成一个完整的 HTTP 消息，方便处理。参数 1024 * 64 表示消息的最大大小为 64KB。
                                    .addLast(new HttpObjectAggregator(1024 * 64))

                                    //处理 WebSocket 协议升级（从 HTTP 协议升级到 WebSocket 协议），并处理握手和控制帧
                                    //（如关闭、ping、pong）。参数 /group 表示 WebSocket 的 URL 路径。
                                    .addLast(new WebSocketServerProtocolHandler(path, null,
                                            true, maxFrameSize, false, true))

                                    //自定义HTTP处理器
                                    .addLast(handlerHttp)

                                    //这是一个自定义的处理器，处理 WebSocket 消息。
                                    .addLast(handlerWebSocket)


                                    //心跳检测
                                    /**
                                     * 参数一：readerIdleTimeSeconds: 读超时。即当在指定的时间间隔内没有从 Channel 读取到数据时,
                                     * 会触发一个 READER_IDLE 的 IdleStateEvent 事件。
                                     *
                                     * 参数二：writerIdleTimeSeconds: 写超时。即当在指定的时间间隔内没有数据写入到 Channel 时,
                                     * 会触发一个 WRITER_IDLE 的 IdleStateEvent 事件。
                                     *
                                     * 参数三：allIdleTimeSeconds: 读/写超时。即当在指定的时间间隔内没有读或写操作时，
                                     * 会触发一个 ALL_IDLE 的 IdleStateEvent 事件。
                                     */
                                    .addLast(new IdleStateHandler(600, 0, 0, TimeUnit.SECONDS))

                                    //心跳超时处理
                                    .addLast(handleHeartBeat);
                        }
                    });
            ChannelFuture future = serverBootstrap.bind(port).sync();
            logger.info("Netty启动成功！");
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            logger.error("Netty启动失败：" + e.getMessage());
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}
